﻿#ifndef MODBUSTCPWORKER_H
#define MODBUSTCPWORKER_H

#include <QObject>
#include <QModbusTcpClient>
#include <QDebug>
#include <QTimer>
#include <QModbusReply>
#include <QQueue>
#include <QSharedPointer>
#include <QTcpSocket>
#include "common/DataType.h"


#if TRADITION_TCP //传统的TCP
class ModBusTcpWorker : public QObject
{
    Q_OBJECT
public:
    explicit ModBusTcpWorker(QObject *parent = nullptr,bool isRTU = false);
    ~ModBusTcpWorker();
public slots:

    //1.设置项目名称和设备编号，预留记录用
    void slotSetProNameDevType(QString proName,int DevType);

    //2.设置IP地址和端口号，如果已有IP和端口在运行则切换
    void slotSetIPPort(QString address,short port);

    //3.1第一次读取寄存器的起始地址和寄存器的个数  寄存器默认为4 输入寄存器  地址可能不连续，所以用列表
    void slotFristReadStartAddressAndRegisterCount(QList<int> startAddress, QList<int> registerCount,int RegisterType = 4);

    //3.设置读取寄存器的起始地址和寄存器的个数  寄存器默认为4 输入寄存器  地址可能不连续，所以用列表
    void slotSetReadStartAddressAndRegisterCount(QList<int> startAddress, QList<int> registerCount,int RegisterType = 4);

    //4.开始连接IP设备或关闭
    void slotStartConnection(bool isStart);


    //添加数据到写数据的队列中
    void slotAddWriteQueue(WriteData* data);


    //直接 写入寄存器的起始地址和寄存器的个数,目前不使用
   // void slotSetWriteStartAddressAndRegisterCount(int startAddress, int registerCount,QList<quint16> data);

    //状态槽函数 连接开始触发定时器循环读
    //void slot_stateChanged(QModbusDevice::State state);
    //void slot_errorOccurred(QModbusDevice::Error error);

signals:
    void sig_WriteDataQueueFinish();//写队列完成

    void sig_readData(QList<quint16> data);    //发送读到的原始数据


    void sig_ConnectionState(bool State);
    void sig_readfloatData(int MsgId,QList<float> data); //发送解析后浮点数据
    void sig_readIntData(int MsgId,QList<int> data); //发送解析后浮点数据
    void sig_readBoolData(int MegId,QList<bool> data);  //发送解析后bool数据

    //void sig_ReadState(bool isState);


private slots:
    void timeoutLoopConnect();//循环连接

    void timeoutReadHoldingRegisters();//周期读寄存器
    void timeoutWriteQueueRegisters();//写队列

protected:
    //建立通信后的读取
    void fristReadHoldingRegisters();

    void readHoldingRegisters();
    void writeHoldingRegisters();

private:

    void FristConHandleData(int RegistersType,int MsgId, QList<quint16> readData);//第一次连接解析readData
    void HandleData(quint8 RegistersType,quint16 MsgId, QList<quint16> readData);//解析readData
//    void HandleData2(QList<quint16> readData);//解析readData

private slots:
    void onConnected();//TCP
    void onReadyRead();//
    void onDisConnected();//

private:
//    QModbusTcpClient* modbusClient;
    //事务ID做消息ID     从站ID        功能码                起始地址   和寄存器个数
    QByteArray HandleAndSend(quint16 transactionId,quint8 unitId,quint8 functionCode,quint16 StartingAddress,quint16 Registers);
    ModBusRTUResponse HandleRec(QByteArray byteArray);
    bool m_RTU;

    QTcpSocket *socket;

    QTimer* m_Connect;//连接定时器

    QTimer* m_LoopReadTimer;
    QTimer* m_WriteQueueTimer;

    QString m_ProName;  //项目名称
    /***type  0:主机  1:从机1   2:从机2  3:从机3 ...  ***/
    int m_DevType;    //设置类型

    QString IPAddress;//IP地址
    short IPPort;    //IP端口

    QList<int> ReadStartAddress; //  循环读取的寄存器的其实地址列表
    QList<int> ReadRegisterCount;//  循环读取的寄存器的其实地址个数

    /***key是寄存器的类型，01 线圈 02离散输入 03 保持寄存器 04输入寄存器
     ***value是读取寄存器的起始地址和寄存器的个数********************/
    QMap<quint8,QList<QPair<int,int> > >  ReadRegisterInfo;

    QMap<quint8,QList<QPair<int,int> > >  fristReadRegisterInfo;//第一次连接需要读取的寄存器

    QVector<QByteArray>  SendReadRegister;     //第一次读的字节

    QVector<QByteArray>  SendfristReadRegister;//循环读的字节

    QQueue<QByteArray>   WriteQueue;//写队列

    quint8 cntSendIndex;

    quint16 WriteStartAddress;
    quint16 WriteRegisterCount;

//    QList<quint16> readData1;//存要转浮点的数据
//    QList<quint16> readData2;//存要转bool的数据


    QList<quint16> writeData;

    QModbusReply *reply;

    quint8  connectCnt;//连接失败的打印频率

    QQueue<WriteData*> m_WriteDataQueue;//写数据的队列，实现写的时候不读数据，TCP连接上的时触发使用

    //记录通信连接上后是不是有写完毕的
    bool WriteDataQueueIsEmpty;
    bool WriteDataQueueIsEmptyPre;

    quint16 logcnt;//

    quint16 m_TransactionId;


};


#else

class ModBusTcpWorker : public QObject
{
    Q_OBJECT
public:
    explicit ModBusTcpWorker(QObject *parent = nullptr);
    ~ModBusTcpWorker();
public slots:

    //1.设置项目名称和设备编号，预留记录用
    void slotSetProNameDevType(QString proName,int DevType);

    //2.设置IP地址和端口号，如果已有IP和端口在运行则切换
    void slotSetIPPort(QString address,short port);

    //3.设置读取寄存器的起始地址和寄存器的个数  寄存器默认为4 输入寄存器  地址可能不连续，所以用列表
    void slotSetReadStartAddressAndRegisterCount(QList<int> startAddress, QList<int> registerCount,int RegisterType = 4);

    //4.开始循环读寄存器
    void slotStartLoopReadRegister(bool isStart);

    //4-1.初始化读取寄存器的起始地址和寄存器的个数  寄存器默认为4 输入寄存器  地址可能不连续，所以用列表
    void slotIniReadStartAddressAndRegisterCount(QList<int> startAddress, QList<int> registerCount,int RegisterType = 4);

    //4-2添加数据到写数据的队列中  掉电记忆在上位机的可以使用，通信连接则执行写消息队列
    void slotAddWriteQueue(WriteData* data);

    //设置写入寄存器的起始地址和寄存器的个数
    void slotSetWriteStartAddressAndRegisterCount(int startAddress, int registerCount,QList<quint16> data);

    //状态槽函数
    void slot_stateChanged(QModbusDevice::State state);
    void slot_errorOccurred(QModbusDevice::Error error);

signals:
    void sig_WriteDataQueueFinish();//写队列完成

    void sig_readData(QList<quint16> data);    //发送读到的原始数据

    void sig_ConnectionState(bool State);
    void sig_readfloatData(int MsgId,QList<float> data); //发送解析后浮点数据
    void sig_readBoolData(int MegId,QList<bool> data);  //发送解析后bool数据


private slots:
    void timeoutReadHoldingRegisters();//周期读寄存器

protected:
    void readHoldingRegisters();
    void writeHoldingRegisters();

private:
    bool startModBusTcpConnect();//开始连接PLC
    void HandleData(int RegistersType,int MsgId, QList<quint16> readData);//解析readData
//    void HandleData2(QList<quint16> readData);//解析readData

private:
//    QModbusTcpClient* modbusClient;
    QSharedPointer<QModbusTcpClient> modbusClient;

    QTimer* timer;

    QString m_ProName;  //项目名称
    /***type  0:主机  1:从机1   2:从机2  3:从机3 ...  ***/
    int m_DevType;    //设置类型

    QString IPAddress;//IP地址
    short IPPort;    //IP端口

    QList<int> ReadStartAddress; //  循环读取的寄存器的其实地址列表
    QList<int> ReadRegisterCount;//  循环读取的寄存器的其实地址个数

    /***key是寄存器的类型，01 线圈 02离散输入 03 保持寄存器 04输入寄存器
     ***value是读取寄存器的起始地址和寄存器的个数********************/
    QMap<int,QList<QPair<int,int> > >  ReadRegisterInfo;

    int WriteStartAddress;
    int WriteRegisterCount;

//    QList<quint16> readData1;//存要转浮点的数据
//    QList<quint16> readData2;//存要转bool的数据


    QList<quint16> writeData;

    QModbusReply *reply;

    quint8  connectCnt;//连接失败的打印频率

    QQueue<WriteData*> m_WriteDataQueue;//写数据的队列，实现写的时候不读数据，TCP连接上的时触发使用

    quint64  m_TimerCnt;

    //记录通信连接上后是不是有写完毕的
    bool WriteDataQueueIsEmpty;
    bool WriteDataQueueIsEmptyPre;

};
#endif

#endif // MODBUSTCPWORKER_H

